<?php

declare(strict_types=1);

namespace app\service\notify;

use app\model\NotifyMessage;
use app\model\NotifyRecipient;
use InvalidArgumentException;
use think\facade\Db;
use think\Paginator;

class RecordService
{
    private const MESSAGE_STATUS_LABELS = [
        NotifyMessage::STATUS_PENDING => '待发送',
        NotifyMessage::STATUS_SENT => '已发送',
    ];

    private const RECIPIENT_STATUS_LABELS = [
        NotifyRecipient::STATUS_PENDING => '待发送',
        NotifyRecipient::STATUS_SENT => '已发送',
        NotifyRecipient::STATUS_FAILED => '发送失败',
        NotifyRecipient::STATUS_READ => '已读',
        NotifyRecipient::STATUS_CANCELLED => '已取消',
    ];

    public function paginate(array $filters, int $page, int $pageSize): Paginator
    {
        $query = NotifyMessage::with(['template'])
            ->withCount(['recipients'])
            ->order('id', 'desc');

        if (!empty($filters['keyword'])) {
            $keyword = trim((string) $filters['keyword']);
            $query->where(static function ($subQuery) use ($keyword) {
                $subQuery->whereLike('title', '%' . $keyword . '%')
                    ->whereOr('content', 'like', '%' . $keyword . '%');
            });
        }

        if (($filters['status'] ?? '') !== '') {
            $query->where('status', (int) $filters['status']);
        }

        if (($filters['template_id'] ?? '') !== '') {
            $query->where('template_id', (int) $filters['template_id']);
        }

        return $query->paginate([
            'list_rows' => $pageSize,
            'page' => $page,
        ]);
    }

    public function findById(int $id): ?NotifyMessage
    {
        return NotifyMessage::with(['template', 'recipients'])->find($id);
    }

    public function format(NotifyMessage $message, bool $withRecipients = true): array
    {
        $status = (int) $message->status;

        $payload = $message->payload;
        if (!is_array($payload)) {
            $payload = $payload ? (array) json_decode((string) $payload, true) : [];
        }
        if (!is_array($payload)) {
            $payload = [];
        }

        $recipientsCount = null;
        if (method_exists($message, 'getData')) {
            $recipientsCount = $message->getData('recipients_count');
        }

        if ($recipientsCount !== null) {
            $recipientsCount = (int) $recipientsCount;
        } elseif ($withRecipients && isset($message->recipients) && is_iterable($message->recipients)) {
            $recipientsCount = count($message->recipients);
        } else {
            $recipientsCount = null;
        }

        $result = [
            'id' => (int) $message->id,
            'template_id' => $message->template_id ? (int) $message->template_id : null,
            'title' => $message->title,
            'content' => $message->content,
            'payload' => $payload,
            'status' => $status,
            'status_label' => self::MESSAGE_STATUS_LABELS[$status] ?? '未知',
            'sent_at' => $message->sent_at,
            'create_time' => $message->create_time,
            'update_time' => $message->update_time,
            'recipients_count' => $recipientsCount,
        ];

        if (isset($message->template) && $message->template) {
            $result['template'] = [
                'id' => (int) $message->template->id,
                'name' => $message->template->name,
                'type' => $message->template->type,
                'title' => $message->template->title,
            ];
        } else {
            $result['template'] = null;
        }

        if ($withRecipients) {
            $recipients = [];
            if (isset($message->recipients) && $message->recipients) {
                foreach ($message->recipients as $recipient) {
                    if ($recipient instanceof NotifyRecipient) {
                        $recipients[] = $this->formatRecipient($recipient);
                    }
                }
            }
            $result['recipients'] = $recipients;
        }

        return $result;
    }

    /**
     * @param iterable<int, NotifyMessage>|array<int, NotifyMessage> $messages
     */
    public function formatCollection(iterable $messages, bool $withRecipients = false): array
    {
        $results = [];
        foreach ($messages as $message) {
            if ($message instanceof NotifyMessage) {
                $results[] = $this->format($message, $withRecipients);
            }
        }

        return $results;
    }

    public function resend(NotifyMessage $message, ?array $recipientIds = null): NotifyMessage
    {
        return Db::transaction(function () use ($message, $recipientIds) {
            $recipientQuery = NotifyRecipient::where('message_id', $message->id);

            if ($recipientIds !== null) {
                if ($recipientIds === []) {
                    throw new InvalidArgumentException('请选择需要重发的接收者');
                }

                $recipientIds = array_values(array_unique(array_map('intval', $recipientIds)));

                $existsCount = NotifyRecipient::where('message_id', $message->id)
                    ->whereIn('id', $recipientIds)
                    ->count();

                if ($existsCount === 0) {
                    throw new InvalidArgumentException('接收者不存在');
                }

                if ($existsCount !== count($recipientIds)) {
                    throw new InvalidArgumentException('部分接收者不存在');
                }

                $recipientQuery->whereIn('id', $recipientIds);
            }

            $updatePayload = [
                'status' => NotifyMessage::STATUS_PENDING,
            ];

            if (NotifyMessage::supportsSentAt()) {
                $updatePayload['sent_at'] = null;
            }

            $message->save($updatePayload);

            $recipientQuery->update([
                'status' => NotifyRecipient::STATUS_PENDING,
                'send_attempts' => 0,
                'read_time' => null,
            ]);

            return $this->findById((int) $message->id);
        });
    }

    public function markAsRead(int $messageId, int $targetId, string $targetType = 'user', ?int $recipientId = null): NotifyRecipient
    {
        return Db::transaction(function () use ($messageId, $targetId, $targetType, $recipientId) {
            $query = NotifyRecipient::where('message_id', $messageId)
                ->where('target_id', $targetId)
                ->where('target_type', $targetType);

            if ($recipientId !== null) {
                $query->where('id', $recipientId);
            }

            /** @var NotifyRecipient|null $recipient */
            $recipient = $query->lock(true)->find();
            if (!$recipient) {
                throw new InvalidArgumentException('通知接收记录不存在');
            }

            if ((int) $recipient->status !== NotifyRecipient::STATUS_READ || empty($recipient->read_time)) {
                $recipient->status = NotifyRecipient::STATUS_READ;
                $recipient->read_time = date('Y-m-d H:i:s');
                $recipient->save();
            }

            return $recipient->refresh();
        });
    }

    public function formatRecipient(NotifyRecipient $recipient): array
    {
        $status = (int) $recipient->status;
        return [
            'id' => (int) $recipient->id,
            'message_id' => (int) $recipient->message_id,
            'target_type' => $recipient->target_type,
            'target_id' => (int) $recipient->target_id,
            'channel' => $recipient->channel,
            'status' => $status,
            'status_label' => self::RECIPIENT_STATUS_LABELS[$status] ?? '未知',
            'send_attempts' => (int) $recipient->send_attempts,
            'read_time' => $recipient->read_time,
            'create_time' => $recipient->create_time,
            'update_time' => $recipient->update_time,
        ];
    }
}
